package cz.cuni.mff.ufal.volk.il;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * A single node in an intermediate language expression.
 *
 * @author Bartłomiej Etenkowski
 */
public class Node {

  /**
   * <p>Constructs a new node.</p>
   *
   * @param  word the main word
   * @param  attributes attributes
   * @param  children children nodes
   *
   * @throws NullPointerException if the {@code word} parameter is {@code null}
   */
  public Node(String word, Map<String, String> attributes, Map<String, List<Node>> children) {
    word.getClass();
    this.word = word;
    if (attributes == null) {
      this.attributes = Collections.unmodifiableMap(new HashMap<String, String>(1));
    } else {
      this.attributes = Collections.unmodifiableMap(new HashMap<String, String>(attributes));
    }
    if (children == null) {
      this.children = Collections.unmodifiableMap(new HashMap<String, List<Node>>(1));
    } else {
      Map<String, List<Node>> auxch = new HashMap<String, List<Node>>(children.size());
      for (Entry<String, List<Node>> e : children.entrySet()) {
        auxch.put(e.getKey(), Collections.unmodifiableList(new ArrayList<Node>(e.getValue())));
      }
      this.children = Collections.unmodifiableMap(auxch);
    }
  }

  private final String word;
  private final Map<String, String> attributes;
  private final Map<String, List<Node>> children;

  /**
   * Gets the underlying word.
   * @return the underlying word
   */
  public String getWord() {
    return word;
  }

  /**
   * Gets the attributes of this item.
   * @return the attributes
   */
  public Map<String, String> getAttributes() {
    return attributes;
  }

  /**
   * Gets an unmodifiable map of the children of this node.
   * @return the children of this node
   */
  public Map<String, List<Node>> getChildren() {
    return children;
  }

  @Override
  public String toString() {
    return createString();
  }

  /**
   * Generates and returns the input for LaTeX qtree package.
   * @return
   */
  public String toQtree() {
  	return String.format("\\Tree %s", toQtreeImpl(null));
  }

  private String toQtreeImpl(String argName) {
  	StringBuilder sb = new StringBuilder();
  	toQtreeImpl(argName, sb);
  	return sb.toString();
  }

  private void toQtreeImpl(String argName, StringBuilder sb) {
  	sb.append(" [.{");
  	if (argName != null)
  		sb.append(toLatexString(argName) + "\\\\");
  	sb.append(String.format("\\textbf{%s}\\\\", toLatexString(word)));
  	sb.append(getAttrString());
  	sb.append("}");
  	for (Entry<String, List<Node>> childList : children.entrySet()) {
  		String key = childList.getKey();
  		for (Node child : childList.getValue())
  			child.toQtreeImpl(key, sb);
  	}
  	sb.append(" ]");
  }

  private String getAttrString() {
  	StringBuilder sb = new StringBuilder();
  	sb.append("$[$");
  	boolean first = true;
  	for (Entry<String, String> e : attributes.entrySet()) {
  		if (first)
  			first = false;
  		else
  			sb.append(", ");
  		sb.append(String.format("%s=%s", toLatexString(e.getKey()), toLatexString(e.getValue())));
  	}
  	sb.append("$]$");
  	return sb.toString();
  }

  private static String toLatexString(String s) {
  	return s.replace("_", "\\_");
  }

  private String createString() {
  	String attributeString = attributes.toString();
  	String childrenString = children.toString();
  	return String.format("node(%s, ATTRIBUTES:%s, CHILDREN:%s)", word, attributeString, childrenString);
  }

}
